﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Abp.Authorization;
using Abp.Collections.Extensions;
using Abp.Extensions;
using AbpProjectTemplate.Application;
using AbpProjectTemplate.Components.Authorization;
using AbpProjectTemplate.Domain.Users;
using AbpProjectTemplate.Utilities.Enums;
using Castle.Core.Internal;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using NUglify.Helpers;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions;
using Unchase.Swashbuckle.AspNetCore.Extensions.Options;

namespace AbpProjectTemplate.Web.Swagger
{
    public static class SwaggerConfig
    {
        public const string Title = "AbpProjectTemplate Api Docs";
        public const string PublicApiName = "Public";
        public static Dictionary<string, string> Groups = new Dictionary<string, string>();

        private static Dictionary<string, IEnumerable<IAbpAuthorizeAttribute>> _actionAuthAttrs = new Dictionary<string, IEnumerable<IAbpAuthorizeAttribute>>();
        static SwaggerConfig()
        {
            Groups.Add(PublicApiName, "公共API");
            foreach (var item in EnumHelper.GetItems(typeof(UserType)).OrderByDescending(t => t.Value))
            {
                Groups.Add(item.Name, $"{item.Desc}API");
            }
        }
        /// <summary>
        /// 获取接口对应的验证attr
        /// </summary>
        private static IEnumerable<IAbpAuthorizeAttribute> GetAuthAttrs(ControllerActionDescriptor actionDescriptor)
        {
            var actAttrs = actionDescriptor.MethodInfo.GetCustomAttributes();
            if (actAttrs.Any(t => t is IAbpAllowAnonymousAttribute))
                return null;
            var actionAuthAttrs = actAttrs
                .Where(t => t is IAbpAuthorizeAttribute)
                .Select(t => t as IAbpAuthorizeAttribute);

            var ctlAttrs = actionDescriptor.ControllerTypeInfo.GetCustomAttributes();
            if (ctlAttrs.Any(t => t is IAbpAllowAnonymousAttribute) && actionAuthAttrs.IsNullOrEmpty())
                return null;

            var controllerAuthAttrs = ctlAttrs
               .Where(t => t is IAbpAuthorizeAttribute)
               .Select(t => t as IAbpAuthorizeAttribute);

            var attrs = actionAuthAttrs.Union(controllerAuthAttrs).Distinct();
            _actionAuthAttrs[actionDescriptor.DisplayName] = attrs;
            return attrs;
        }
        /// <summary>
        /// 检测当前doc是否有显示此action api的权限
        /// </summary>
        private static bool HasPermissions(string docName, ControllerActionDescriptor action)
        {
            var attrs = _actionAuthAttrs.ContainsKey(action.DisplayName) ?
                _actionAuthAttrs[action.DisplayName] :
                GetAuthAttrs(action);
            //如果不需要权限，所有组都显示
            if (attrs.IsNullOrEmpty())
                return true;

            if (docName == PublicApiName)
                return attrs.IsNullOrEmpty();

            if (Enum.TryParse<UserType>(docName, out var userType))
            {
                return attrs.Any(t => t.Permissions.IsNullOrEmpty()
                                      || t.Permissions.Any(x => PermissionHelper.HasUserType(x, userType)));

            }
            return false;
        }
        /// <summary>
        /// 生成配置
        /// </summary>
        public static void Configure(SwaggerGenOptions options)
        {
            foreach (var group in Groups)
                options.SwaggerDoc(group.Key, new OpenApiInfo { Title = group.Value, Version = "v1" });
            options.DocInclusionPredicate((docName, description) =>
            {
                var action = description.ActionDescriptor as ControllerActionDescriptor;
                return HasPermissions(docName, action);
            });
            //获取执行目录下的所有解释文件.xml
            var xmlCommentsFiles = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.xml");
            xmlCommentsFiles.ForEach(file =>
            {
                options.IncludeXmlCommentsWithRemarks(file, true);
            });
            //options.SchemaFilter<SwaggerEnumFilter>();
            options.AddEnumsWithValuesFixFilters(opts =>
            {
                opts.IncludeXEnumRemarks = true;
                opts.IncludeDescriptions = true;
                xmlCommentsFiles.ForEach(file =>
                {
                    opts.IncludeXmlCommentsFrom(file);
                });
                opts.DescriptionSource = DescriptionSources.DescriptionAttributesThenXmlComments;
            });
            //自定义OperationId
            options.CustomOperationIds(apiDesc =>
            {
                var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
                return controllerAction.ControllerName + "-" + controllerAction.ActionName;
            });
            var abpLocalAuth = new OpenApiSecurityScheme()
            {
                Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
                Name = "Authorization",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.ApiKey,
                Extensions = new Dictionary<string, IOpenApiExtension>() {
                        {
                            "LoginForm",new OpenApiObject(){
                                { "Content-Type",new OpenApiString("application/json")},
                                { "LoginUrl",new OpenApiString("/api/Authentication/Password")},
                                { "Items",new OpenApiObject(){
                                        { "TenantId",new OpenApiString("") },
                                        { "UserNameOrEmailAddress",new OpenApiString("") },
                                        { "Password",new OpenApiPassword("password") }
                                     }
                                }

                            }
                        }
                    }
            };
            options.AddSecurityDefinition("AbpLocalAuth", abpLocalAuth);
            // Define the BearerAuth scheme that's in use
            //options.AddSecurityDefinition("AbpLocalAuth", new OpenApiSecurityScheme()
            //{
            //    Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
            //    Name = "Authorization",
            //    In = ParameterLocation.Header,
            //    Type = SecuritySchemeType.ApiKey,
            //    Extensions = new Dictionary<string, IOpenApiExtension>() {
            //        {
            //            "LoginForm",new OpenApiObject(){
            //                { "Content-Type",new OpenApiString("application/json")},
            //                { "LoginUrl",new OpenApiString("http://localhost:21021/api/TokenAuth/Authenticate")},
            //                { "Items",new OpenApiObject(){
            //                        { "TenantId",new OpenApiString("") },
            //                        { "UserNameOrEmailAddress",new OpenApiString("") },
            //                        { "Password",new OpenApiPassword("password") }
            //                     } 
            //                }

            //            }
            //        }
            //    }
            //});
          
        }
        /// <summary>
        /// UI配置
        /// </summary>
        public static void ConfigureUI(SwaggerUIOptions options)
        {
            options.DocumentTitle = Title;
            foreach (var group in Groups)
            {
                options.SwaggerEndpoint($"/swagger/{group.Key}/swagger.json", $"{group.Value}");
            }
            options.IndexStream = () => typeof(SwaggerConfig).Assembly
                    .GetManifestResourceStream($"{typeof(SwaggerConfig).Namespace}.index.html");
        }
    }
}
